package com.wlf.server.common.service;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.wlf.server.common.context.BeanContext;
import com.wlf.server.common.em.CacheKey;
import com.wlf.server.common.em.Const;
import com.wlf.server.common.entity.*;
import com.wlf.server.common.mapper.SysUserMapper;
import com.wlf.server.web.dto.MappingDTO;
import com.wlf.server.web.dto.Option;
import com.wlf.server.web.dto.ParamPage;
import com.wlf.server.web.dto.UserDTO;
import com.wlf.server.web.util.AjaxBean;
import com.wlf.server.web.util.AjaxError;
import com.wlf.server.web.util.JSON;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@CacheConfig(cacheNames = CacheKey.user)
@RequiredArgsConstructor
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {
    final SysRoleService roleService;
    final SysSerialService serialService;
    final SysAttachmentService attachmentService;
    final SysOrganService organService;
    final SysDeptService deptService;
    final SysPostService postService;
    final SysMenuService menuService;
    @Resource
    MappingDTO mappingDTO;



    /**
     * 默认初始密码
     */
    public final static String passRes = "000000";
    public final static String adminId = "1526923684416102402";

    public AjaxBean list(UserDTO userDTO, ParamPage paramPage) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotBlank(userDTO.getName())) {
            wrapper.like(SysUser::getName, userDTO.getName()).or();
            wrapper.like(SysUser::getLoginNo, userDTO.getName()).or();
            wrapper.like(SysUser::getPhone, userDTO.getName());
        }
        wrapper.orderByAsc(SysUser::getId);
        Page<SysUser> page = this.page(Page.of(paramPage.getCurrent(), paramPage.getSize()), wrapper);
        List<SysUser> list = page.getRecords();

        ArrayList<UserDTO> userDTOS = new ArrayList<>();
        for (SysUser user : list) {
            UserDTO dto = mappingDTO.toDTO(user);
            // 补充用户里的信息
            dto = BeanContext.getBean(this.getClass()).getUser(dto.getId());
            userDTOS.add(dto);
        }
        // 覆盖写入Data
        return AjaxBean.getOkPage(page).setData(userDTOS);
    }

    @CacheEvict(key = "#userDTO.id", condition = "#userDTO.id != null")
    public AjaxBean saveOrUpdate(UserDTO userDTO) {
        if (userDTO.getRoleIds() == null) {
            return AjaxBean.getError("角色信息不能为空");
        }
        SysUser user = mappingDTO.to(userDTO);
        if (StrUtil.isBlank(user.getId())) {
            if (this.lambdaQuery().eq(SysUser::getPhone, user.getPhone()).count() != 0) {
                return AjaxBean.getError("手机号码重复");
            }
            user.setLoginNo(Const.UserType.valueOf(user.getUserType())
                    + serialService.getSerial(Const.SerialType.LOGIN_NO));
        }
        // 默认密码六个0
        user.setPassword(passRes);
        // 保存用户
        super.saveOrUpdate(user);
        // 删除此用户所有的角色信息
        Db.lambdaUpdate(RoleUser.class).eq(RoleUser::getUserId, user.getId()).remove();

        // 保存用户角色信息
        Db.saveBatch(
                userDTO.getRoleIds().stream().map(i -> {
                    RoleUser roleUser = new RoleUser();
                    roleUser.setUserId(user.getId());
                    roleUser.setRoleId(i);
                    return roleUser;
                }).toList()
        );

        // 删除用户所有机构信息
        Db.lambdaUpdate(OrganUser.class).eq(OrganUser::getUserId, user.getId()).remove();

        // 保存机构信息
        Db.saveBatch(
                userDTO.getOrganIds().stream().map(i -> {
                    OrganUser ou = new OrganUser();
                    ou.setUserId(user.getId());
                    ou.setOrganId(i);
                    return ou;
                }).toList()
        );

        // 删除用户所有岗位信息
        Db.lambdaUpdate(PostUser.class).eq(PostUser::getUserId, user.getId()).remove();

        // 保存g岗位信息
        Db.saveBatch(
                userDTO.getPostIds().stream().map(i -> {
                    PostUser postUser = new PostUser();
                    postUser.setUserId(user.getId());
                    postUser.setPostId(i);
                    return postUser;
                }).toList()
        );
        // 删除用户所有部门信息
        Db.lambdaUpdate(DeptUser.class).eq(DeptUser::getUserId, user.getId()).remove();

        // 保存部门信息
        Db.saveBatch(
                userDTO.getDeptIds().stream().map(i -> {
                    DeptUser deptUser = new DeptUser();
                    deptUser.setUserId(user.getId());
                    deptUser.setDeptId(i);
                    return deptUser;
                }).toList()
        );
        return AjaxBean.getOkMsg();
    }

    /**
     * 更新用户状态
     *
     * @param userDTO 用户DTO
     * @return 操作结果
     */
    @CacheEvict(key = "#userDTO.id")
    public AjaxBean updateStatus(UserDTO userDTO) {
        SysUser user = new SysUser();
        user.setId(userDTO.getId());
        user.setStatus(userDTO.getStatus());
        return AjaxBean.getByBool(this.updateById(user));
    }

    /**
     * 单个获取
     */

    public AjaxBean get(String id) {
        return AjaxBean.getOkData(BeanContext.getBean(this.getClass()).getUser(id));
    }

    /**
     * 根据主键获取用户信息
     *
     * @param id 用户主键
     * @return 用户信息
     */
    @Cacheable(key = "#id")
    public UserDTO getUser(String id) {
        UserDTO userDTO = mappingDTO.toDTO(this.getById(id));
        userDTO.setTypeName(Const.UserType.valueOf(userDTO.getUserType()).getDesc());

        // 写入角色信息
        List<String> roleIds = Db.lambdaQuery(RoleUser.class)
                .eq(RoleUser::getUserId, id).list()
                .stream().map(RoleUser::getRoleId).toList();
        userDTO.setRoleIds(roleIds);
        if (ObjectUtil.isNotEmpty(roleIds)) {
            if (id.equals(adminId)) {
                userDTO.setRoleList(mappingDTO.toRoleDTO(roleService.list()));
            } else {
                userDTO.setRoleList(mappingDTO.toRoleDTO(roleService.listByIds(roleIds)));
            }

        } else {
            userDTO.setRoleList(ListUtil.empty());
        }
        // 写入权限信息

        // 获取菜单信息
        List<String> menuIds = Db.lambdaQuery(RoleMenu.class)
                .in(RoleMenu::getRoleId, roleIds).list()
                .stream().map(RoleMenu::getMenuId).toList();

        if (ObjectUtil.isEmpty(menuIds)) {
            userDTO.setMenuIds(ListUtil.empty());
        } else {
            if (id.equals(adminId)) {
                userDTO.setMenuList(mappingDTO.toMenuDTO(menuService.lambdaQuery().list()));
            } else {
                userDTO.setMenuList(mappingDTO.toMenuDTO(menuService.lambdaQuery().in(SysMenu::getId, menuIds)
                        .list()));
            }

        }
        // 写入机构信息
        List<String> organIds = Db.lambdaQuery(OrganUser.class).eq(OrganUser::getUserId, id).list()
                .stream().map(OrganUser::getOrganId).collect(Collectors.toList());
        userDTO.setOrganIds(organIds);

        if (ObjectUtil.isNotEmpty(organIds)) {
            userDTO.setOrganList(mappingDTO.toOrganDTO(organService.listByIds(organIds)));
        } else {
            userDTO.setOrganList(ListUtil.empty());
        }

        // 写入岗位信息
        List<String> postIds = Db.lambdaQuery(PostUser.class).eq(PostUser::getUserId, id).list()
                .stream().map(PostUser::getPostId).collect(Collectors.toList());
        userDTO.setPostIds(postIds);

        if (ObjectUtil.isNotEmpty(postIds)) {
            userDTO.setPostList(mappingDTO.toPostDTO(postService.listByIds(postIds)));
        } else {
            userDTO.setPostList(ListUtil.empty());
        }
        // 写入部门信息

        List<String> deptIds = Db.lambdaQuery(DeptUser.class).eq(DeptUser::getUserId, id).list()
                .stream().map(DeptUser::getDeptId).collect(Collectors.toList());
        userDTO.setDeptIds(deptIds);

        if (ObjectUtil.isNotEmpty(deptIds)) {
            userDTO.setDeptList(mappingDTO.toDeptDTO(deptService.listByIds(deptIds)));
        } else {
            userDTO.setDeptList(ListUtil.empty());
        }
        return userDTO;
    }

    /**
     * 单个删除，
     */
    @CacheEvict(key = "#id")
    public AjaxBean del(String id) {
        if (id.equals(adminId)) {
            AjaxError.getAndThrow("管理员用户不可删除");
        }
        delUserRelation(id);
        return AjaxBean.getByBool(super.removeById(id));
    }

    /**
     * 删除用户的关联信息
     *
     * @param userId 用户ID
     */
    private void delUserRelation(String userId) {
        Db.lambdaUpdate(RoleUser.class).eq(RoleUser::getUserId, userId).remove();
        Db.lambdaUpdate(DeptUser.class).eq(DeptUser::getUserId, userId).remove();
        Db.lambdaUpdate(OrganUser.class).eq(OrganUser::getUserId, userId).remove();
        Db.lambdaUpdate(PostUser.class).eq(PostUser::getUserId, userId).remove();
    }

    /**
     * 批量删除
     */
    @CacheEvict(allEntries = true)
    public AjaxBean delAll(String ids) {
        List<String> userIds = JSON.parseArray(ids, String.class);
        if (userIds.contains(adminId)) {
            AjaxError.getAndThrow("管理员用户不可删除");
        }
        userIds.forEach(this::delUserRelation);
        return AjaxBean.getByBool(super
                .removeBatchByIds(userIds));
    }

    /**
     * 登录用户修改密码
     *
     * @param oldPas 原密码
     * @param newPas 新密码
     * @return 结果
     */
    public AjaxBean changePas(String oldPas, String newPas) {
        UserDTO user = getUserByLogin();
        if (!user.getPassword().equals(oldPas)) {
            return AjaxBean.getError("原始密码错误");
        }
        SysUser updateBean = new SysUser();
        updateBean.setId(user.getId());
        updateBean.setPassword(newPas);
        return AjaxBean.getByBool(this.updateById(updateBean), "密码修改成功", null);
    }


    /**
     * 根据角色ID查找拥有此角色的用户
     */
    public AjaxBean userList(String id, Page<SysUser> page) {
        List<String> userIds = Db.lambdaQuery(RoleUser.class).eq(RoleUser::getRoleId, id).list()
                .stream().map(RoleUser::getUserId).collect(Collectors.toList());
        if (ObjectUtil.isEmpty(userIds)) {
            // 加一个不可能查询到的参数
            userIds.add("-1");
        }
        Page<SysUser> userPage = this.page(page, new LambdaQueryWrapper<SysUser>().in(SysUser::getId, userIds));
        return AjaxBean.getOkPage(userPage);
    }

    /**
     * 获取当前登录用户信息，
     */
    public UserDTO getUserByLogin() {
        // 通过这种方法调用能触发注解缓存
        return BeanContext.getBean(this.getClass()).getUser(StpUtil.getLoginIdAsString());
    }

    /**
     * 上传头像
     *
     * @param file 头像文件
     * @return 文件内容
     */
    @CacheEvict(key = "#id")
    public SysAttachment plusAvatar(MultipartFile file, String id) {
        if (file.getSize() / 1024 > 1000) {
            AjaxError.getAndThrow("头像不应超过1mb");
        }
        SysAttachment attachment = attachmentService.plus(file, "用户上传头像");
        // 更新用户信息
        SysUser user = new SysUser();
        user.setAvatar(attachment.getWebPath());
        user.setId(id);
        this.updateById(user);
        return attachment;
    }

    /**
     * 查询用户
     *
     * @param id，机构ID
     * @param userDTO   查询参数
     * @param paramPage 分页
     * @return 用户结果
     */
    public AjaxBean getUserByOrganId(String id, UserDTO userDTO, ParamPage paramPage) {
        ArrayList<String> organIds = new ArrayList<>();
        // 是否显示子集
        if (userDTO.getShowChildren()) {
            organService.getOrganIds(id, organIds);
        } else {
            organIds.add(id);
        }

        // 查出机构拥有的用户ID ,多对多，先查出所有用户ID
        List<String> userIds = organService.getUserIdsByOrganIds(organIds);

        // 分页获取用户信息
        return getPageUser(userDTO, paramPage, userIds);
    }

    /**
     * 查询用户
     *
     * @param id，根据部门ID
     * @param userDTO   查询参数
     * @param paramPage 分页
     * @return 用户结果
     */
    public AjaxBean getUserByDeptId(String id, UserDTO userDTO, ParamPage paramPage) {
        ArrayList<String> deptIds = new ArrayList<>();
        // 是否显示子集
        if (userDTO.getShowChildren()) {
            deptService.getDeptIds(id, deptIds);
        } else {
            deptIds.add(id);
        }
        // 查出机构拥有的用户ID ,多对多，先查出所有用户ID
        List<String> userIds = deptService.getUserIdsByDeptIds(deptIds);

        // 分页获取用户信息
        return getPageUser(userDTO, paramPage, userIds);
    }


    /**
     * 查询用户
     *
     * @param id，根据部门ID
     * @param userDTO   查询参数
     * @param paramPage 分页
     * @return 用户结果
     */
    public AjaxBean getUserByPostId(String id, UserDTO userDTO, ParamPage paramPage) {
        ArrayList<String> postIds = new ArrayList<>();
        // 是否显示子集
        if (userDTO.getShowChildren()) {
            postService.getPostIds(id, postIds);
        } else {
            postIds.add(id);
        }
        // 查出机构拥有的用户ID ,多对多，先查出所有用户ID
        List<String> userIds = postService.getUserIdsByPostIds(postIds);

        // 分页获取用户信息
        return getPageUser(userDTO, paramPage, userIds);
    }

    /**
     * 获取用户信息
     *
     * @param userDTO   查询参数
     * @param paramPage 分页
     * @param userIds   用户ID
     * @return 用户信息
     */
    private AjaxBean getPageUser(UserDTO userDTO, ParamPage paramPage, List<String> userIds) {
        LambdaQueryChainWrapper<SysUser> wrapper = this.lambdaQuery();
        if (ObjectUtil.isEmpty(userIds)) {
            userIds.add("-1");
        }
        wrapper.in(SysUser::getId, userIds);
        if (StrUtil.isNotBlank(userDTO.getName())) {
            wrapper.like(SysUser::getName, userDTO.getName());
        }
        wrapper.orderByDesc(SysUser::getId);
        Page<SysUser> page = wrapper.page(Page.of(paramPage.getCurrent(), paramPage.getSize()));
        ArrayList<UserDTO> userDTOS = new ArrayList<>();

        for (SysUser record : page.getRecords()) {
            UserDTO dto = mappingDTO.toDTO(record);
            // 补充用户里的信息
            dto = BeanContext.getBean(this.getClass()).getUser(dto.getId());
            userDTOS.add(dto);
        }

        return AjaxBean.getOkData(page).setData(userDTOS);
    }


    /**
     * 获取角色列表
     *
     * @return 角色信息
     */
    public List<Option> getRoleList() {
        List<SysRole> list = roleService.list();
        return list.stream().map(i -> {
            Option option = new Option();
            option.setLabel(i.getName());
            option.setValue(i.getId());
            // 启用状态下应该为不禁用状态
            option.setDisabled(!i.getStatus());
            return option;
        }).toList();
    }

    public List<Tree<String>> getOrganList() {
        List<SysOrgan> list = organService.list();
        return organService.getTree(list);
    }

    public List<Tree<String>> getPostList() {
        List<SysPost> list = postService.list();
        TreeNodeConfig tConfig = new TreeNodeConfig();
        tConfig.setIdKey("value");
        tConfig.setChildrenKey("children");
        tConfig.setNameKey("label");
        tConfig.setWeightKey("sort");
        return postService.getTree(list, tConfig);
    }

    public List<Tree<String>> getDeptList() {
        List<SysDept> list = deptService.list();
        TreeNodeConfig tConfig = new TreeNodeConfig();
        tConfig.setIdKey("value");
        tConfig.setChildrenKey("children");
        tConfig.setNameKey("label");
        tConfig.setWeightKey("sort");
        return deptService.getTree(list, tConfig);
    }

}
